/**
 * @file 装饰器优化
 * @author DuskyHuang 圣子
*/

import "@dusky/ts-common";
import { _decorator, __private } from "cc";
import { isClass } from "./object";
const { property } = _decorator;

type ExposedAttributes = __private._cocos_core_data_utils_attribute_defines__IExposedAttributes;
type ExposedAttrGroup = ExposedAttributes['group'];
type ExposedAttributesPair<Key extends keyof ExposedAttributes = keyof ExposedAttributes>
    = [Key, ExposedAttributes[Key]] | [number, number, number];

/**
 * 编辑器属性提示装饰
 * @param tooltip 提示内容
 * @param type 类型
 * @param displayName 显示名称
 * @param visible 显示函数
 * @params kvs 其他罗列
 * @example
 * ```
 * @tip("tooltip", Node, "displayName", ()=>true, 0, 1, 0.1], ['unit','deg'], ['override', true]) 
 * public attr: Node = null;
 * ```
 */
export function tip(tooltip: string, ...kvs: ExposedAttributesPair[]): PropertyDecorator;
export function tip(tooltip: string, type: object, ...kvs: ExposedAttributesPair[]): PropertyDecorator;
export function tip(tooltip: string, displayName: string, ...kvs: ExposedAttributesPair[]): PropertyDecorator;
export function tip(tooltip: string, visible: Validator, ...kvs: ExposedAttributesPair[]): PropertyDecorator;
export function tip(tooltip: string, type: object, displayName: string, ...kvs: ExposedAttributesPair[]): PropertyDecorator;
export function tip(tooltip: string, visible: Validator, displayName: string, ...kvs: ExposedAttributesPair[]): PropertyDecorator;
export function tip(tooltip: string, type: object, visible: Validator, ...kvs: ExposedAttributesPair[]): PropertyDecorator;
export function tip(tooltip: string, type: object, visible: Validator, displayName: string, ...kvs: ExposedAttributesPair[]): PropertyDecorator;
export function tip(tooltip: string, ...rests: any[]): PropertyDecorator {
    return property(_wrap(tooltip, ...rests));
}

/**
 * 编辑器属性成组装饰
 * @param group eg."组名 标签[.组]"
 * @param tooltip 提示内容
 * @param type 类型
 * @param displayName 显示名称
 * @param visible 显示函数
 * @params kvs 其他装饰属性罗列
 * @example
 * ```
 * @group("tab.group", "tooltip", Node, "displayName", ()=>true, [0, 1, 0.1], ['unit','deg'], ['override', true]) 
 * public attr: Node = null;
 * ```
 */
export function group(group: ExposedAttrGroup, tooltip: string, ...kvs: ExposedAttributesPair[]): PropertyDecorator;
export function group(group: ExposedAttrGroup, tooltip: string, type: object, ...kvs: ExposedAttributesPair[]): PropertyDecorator;
export function group(group: ExposedAttrGroup, tooltip: string, visible: Validator, ...kvs: ExposedAttributesPair[]): PropertyDecorator;
export function group(group: ExposedAttrGroup, tooltip: string, displayName: string, ...kvs: ExposedAttributesPair[]): PropertyDecorator;
export function group(group: ExposedAttrGroup, tooltip: string, type: object, displayName: string, ...kvs: ExposedAttributesPair[]): PropertyDecorator;
export function group(group: ExposedAttrGroup, tooltip: string, visible: Validator, displayName: string, ...kvs: ExposedAttributesPair[]): PropertyDecorator;
export function group(group: ExposedAttrGroup, tooltip: string, type: object, visible: Validator, ...kvs: ExposedAttributesPair[]): PropertyDecorator;
export function group(group: ExposedAttrGroup, tooltip: string, type: object, visible: Validator, displayName: string, ...kvs: ExposedAttributesPair[]): PropertyDecorator;
export function group(group: ExposedAttrGroup, tooltip: string, ...rests: any[]): PropertyDecorator {
    if (isStr(group) && group.indexOf('.') > 0) {
        const [name, id] = group.split('.');
        group = { name, id };
    }
    return property({ group, ..._wrap(tooltip, ...rests) })
}

/**
 * 编辑器属性序列化装饰
 */
export const serialize: PropertyDecorator = property({ serializable: true });

/**
 * 编辑器属性可见装饰
 */
export const visible: PropertyDecorator = property({ visible: true });

/**
 * 编辑器属性不可见装饰
 */
export const invisible: PropertyDecorator = property({ visible: false });

/**
 * 编辑器属性覆盖父类
 */
export const overrode: PropertyDecorator = property({ override: true });

// 装饰器实现函数
function _wrap(tooltip: string, ...kvs: ExposedAttributesPair[]): ExposedAttributes;
function _wrap(tooltip: string, type: object, ...kvs: ExposedAttributesPair[]): ExposedAttributes;
function _wrap(tooltip: string, displayName: string, ...kvs: ExposedAttributesPair[]): ExposedAttributes;
function _wrap(tooltip: string, visible: Validator, ...kvs: ExposedAttributesPair[]): ExposedAttributes;
function _wrap(tooltip: string, type: object, displayName: string, ...kvs: ExposedAttributesPair[]): ExposedAttributes;
function _wrap(tooltip: string, visible: Validator, displayName: string, ...kvs: ExposedAttributesPair[]): ExposedAttributes;
function _wrap(tooltip: string, type: object, visible: Validator, ...kvs: ExposedAttributesPair[]): ExposedAttributes;
function _wrap(tooltip: string, type: object, displayName: string, visible: Validator, ...kvs: ExposedAttributesPair[]): ExposedAttributes;
function _wrap(tooltip: string, ...rests: any[]): ExposedAttributes {
    const props: ExposedAttributes = { tooltip };
    for (let rest of rests) {
        if (Array.isArray(<ExposedAttributes>rest)) {
            if (rest.length >= 3) {
                props['slide'] = true;
                props['range'] = rest;
            } else if (rest.length >= 2) {
                props[rest[0]] = rest[1]
            } else if (rest.length === 1) {
                props['type'] = rest[0];
            }
        }
    }
    const [arg1, arg2, arg3, ...others] = rests;
    if (arg1 && !Array.isArray(arg1)) {
        if (isClass(arg1)) props.type = arg1;
        else if (isStr(arg1)) props.displayName = arg1;
        else if (isFunc(arg1)) props.visible = arg1;
    }
    if (arg2 && !Array.isArray(arg2)) {
        if (isStr(arg2)) props.displayName = arg2;
        else if (isFunc(arg2)) props.visible = arg2;
    }
    if (isStr(arg3)) props.displayName = arg3;
    return props;
}